package jp.aist.gtrc.plus.scheduler;

import org.apache.log4j.Logger;

import java.io.IOException;
import java.net.Socket;
import java.util.Collection;
import java.util.Date;

import jp.aist.gtrc.plus.admission.AdmissionOperationManager;

import jp.aist.gtrc.plus.scheduler.algorithm.Allocation;
import jp.aist.gtrc.plus.scheduler.algorithm.DemandProportion;
import jp.aist.gtrc.plus.scheduler.algorithm.Fifo;
import jp.aist.gtrc.plus.scheduler.algorithm.MostWantedResourceFirst;
//import jp.aist.gtrc.plus.scheduler.algorithm.XXX;
import jp.aist.gtrc.plus.scheduler.algorithm.Scheduler;
import jp.aist.gtrc.plus.scheduler.job.FairshareScheduler;
import jp.aist.gtrc.plus.scheduler.job.TwoLevelScheduler;
import jp.aist.gtrc.plus.scheduler.node.MatchedByOtherExecNodeAllocator;
import jp.aist.gtrc.plus.scheduler.node.NodeAllocator;
import jp.aist.gtrc.plus.scheduler.node.NodeInfo;
import jp.aist.gtrc.plus.scheduler.node.NodeSorter;
import jp.aist.gtrc.plus.scheduler.node.ProvisionSortKey;
import jp.aist.gtrc.plus.scheduler.specific.pbs.status.PBSJobStatus;
import jp.aist.gtrc.plus.scheduler.specific.pbs.util.PBSScheduleOrder;
import jp.aist.gtrc.plus.scheduler.specific.pbs.util.PBSSchedulerCommandType;

import jp.aist.gtrc.plus.scheduler.status.JobStatus;
import jp.aist.gtrc.plus.scheduler.status.MainServer;
import jp.aist.gtrc.plus.scheduler.status.NodeStatus;
import jp.aist.gtrc.plus.scheduler.sys.SchedulerException;

class PBSDynProScheduler extends PBSScheduler {

        protected static final Logger logger = Logger.getLogger(PBSDynProScheduler.class);

        protected Scheduler algo;

   PBSDynProScheduler(SchedulerOptions options) throws Exception {

      super(options);

      algo = new Fifo(nodeAllocator);
      //algo = new MostWantedResourceFirst(nodeAllocator);
      //algo = new DemandProportion(nodeAllocator);
      //algo = new XXX(nodeAllocator);

      System.out.println("PBSDynProScheduler started...");
   }

        protected void schedule(ScheduleOrder order) throws SchedulerException{

                assert(jobMgr != null);
                assert(scheduler != null);

                //logger.debug(order.getClass().getName());

                if (order instanceof PBSScheduleOrder) {
                   PBSSchedulerCommandType cmd = ((PBSScheduleOrder) order).getSchedulerCommand();
                   logger.debug("PBSDynProScheduler schedule() by command " + cmd);
                   System.out.println("PBSDynProScheduler schedule() by command " + cmd + " on " + new Date());
                }

                //System.out.println("PBSDynProScheduler updated status");
                ((AdmissionOperationManager) admissionServer).clearStatusDirtyFlag();
                Collection<JobStatus> allJobs = statusMgr.getJobs();

                //for (JobStatus jobStatus : allJobs) {
                //   PBSJobStatus pbsJobStatus = (PBSJobStatus) jobStatus;
                //   logger.debug(pbsJobStatus.getJobID() + " spent " + pbsJobStatus.getUsedCPUTime() + " sec.");  // resources_used.cput
                //}
                /*
                 * Update fairshare status
                 */
                if (scheduler instanceof FairshareScheduler) {
                   ((FairshareScheduler) scheduler).updateStatus(allJobs); 
                } else if (scheduler instanceof TwoLevelScheduler) {
                   ((TwoLevelScheduler) scheduler).updateStatus(allJobs); 
                }

                Collection<JobStatus> runnableJobs = jobMgr.getRunnableJobs(allJobs);
                /*
                 * Select one job to run
                 */
               Collection<NodeInfo>   usableNodes = nodeMgr.getUnreservedNodes();
               Collection<Allocation> allocs = null;
               try {
                  algo.preSchedule(runnableJobs,usableNodes);
                  allocs = algo.doScheduling(runnableJobs,usableNodes);
                  algo.postSchedule(runnableJobs,usableNodes,allocs);
               } catch (Exception e) {
                  e.printStackTrace();
               } 
               MainServer srv = order.getMainServer();
               for (Allocation alloc : allocs) {
                  JobStatus job = alloc.getJob();
                  Collection<NodeInfo> nodes = alloc.getNodes();
                  srv.runJob(job, nodes);
                  ((AdmissionOperationManager) admissionServer).setStatusDirtyFlag();
               }

               // recycling schedule event
               if ( (allocs != null) && (allocs.size() > 0) ) {
                  Socket s = serverConnector.getAuthorizedSocket();
                  PBSScheduleOrder newOrder = new PBSScheduleOrder(PBSSchedulerCommandType.RECYC,s);
                  starter.runSchedule(newOrder);
               }


        }

        protected NodeAllocator getExecNodeAllocator(SchedulerOptions options) throws SchedulerException {
                NodeSorter nodeSorter = new NodeSorter();
                // local resources first, before using remote and provisioned
                ProvisionSortKey key = ProvisionSortKey.valueOf("Type"); 
                if (key == null) {
                   System.err.println("Unknown provision sort key!");
                   System.exit(1);
                }
                nodeSorter.addSortKey(key);

                return new MatchedByOtherExecNodeAllocator(nodeSorter);

        }


}
